﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Threading;

namespace XTrace
{
	public sealed partial class XTraceEvent
	{
#region Hide object members
		/// <summary />
		[EditorBrowsable(EditorBrowsableState.Never)]
		public override bool Equals(object obj)
		{
			return base.Equals(obj);
		}

		/// <summary />
		[EditorBrowsable(EditorBrowsableState.Never)]
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}

		/// <summary />
		[EditorBrowsable(EditorBrowsableState.Never)]
		public override string ToString()
		{
			return Serialize();
		}

		/// <summary />
		[EditorBrowsable(EditorBrowsableState.Never)]
		public new Type GetType()
		{
			return base.GetType();
		}

		/// <summary>Do not call this method.</summary>
		[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "b")]
		[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "a")]
		[Obsolete("Do not call this method", true)]
		[EditorBrowsable(EditorBrowsableState.Never)]
		public new static bool Equals(object a, object b)
		{
			return object.Equals(a, b);
		}

		/// <summary>Do not call this method.</summary>
		[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "b")]
		[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "a")]
		[Obsolete("Do not call this method", true)]
		[EditorBrowsable(EditorBrowsableState.Never)]
		public new static bool ReferenceEquals(object a, object b)
		{
			return object.ReferenceEquals(a, b);
		}

#endregion

#if PocketPC
		internal const string PocketPCunknownData = "?ppc?";
#endif

		static int? _processIdCache;
		static int ProcessIdCache
		{
			get
			{
				if (!_processIdCache.HasValue)
				{
					_processIdCache = Process.GetCurrentProcess().Id;
				}
				return _processIdCache.Value;
			}
		}

		static string _processNameCache;
		static string ProcessNameCache
		{
			get
			{
				if (_processNameCache == null)
				{
#if !PocketPC
					_processNameCache = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName);
#else
					_processNameCache = PocketPCunknownData;
#endif
				}
				return _processNameCache;
			}
		}

		XTraceEvent(XTraceLevel level, Assembly assembly, string eventId)
		{
			_time = DateTime.UtcNow;
			_processId = ProcessIdCache;
			_processName = ProcessNameCache;
			_threadId = Thread.CurrentThread.ManagedThreadId;
			_threadName = Thread.CurrentThread.Name;

			if (assembly != null)
			{
				_project = assembly.FullName;
			}
			var i = _project.IndexOf(',');
			if (i > 0)
			{
				_project = _project.Substring(0, i);
			}

#if !PocketPC
			if (level <= XTraceLevel.Warning || IsDebug(assembly) || string.IsNullOrEmpty(eventId))
			{
				var goodStack = XTraceSource.GetGoodStack();
				_callStack = goodStack.ToString();
				var frame = goodStack.FrameCount > 0 ? goodStack.GetFrame(0) : null;
				if (frame != null)
				{
					_lineNumber = frame.GetFileLineNumber();
					_sourceFile = frame.GetFileName() ?? string.Empty;
					var mi = frame.GetMethod();
					_nameSpace = mi.DeclaringType.Namespace ?? string.Empty;
					_className = mi.DeclaringType.Name;
					_methodName = mi.Name;
					if (_project == null)
					{
						_project = mi.DeclaringType.Assembly.FullName;
					}
				}
			}
#else
			if (assembly == null && _project == null)
			{
				_project = PocketPCunknownData;
			}
			_callStack = PocketPCunknownData;
			_sourceFile = PocketPCunknownData;
			_nameSpace = PocketPCunknownData;
			_className = PocketPCunknownData;
			_methodName = PocketPCunknownData;
#endif
		}

#if !PocketPC
		static bool IsDebug(Assembly assembly)
		{
			if (assembly == null)
			{
				assembly = Assembly.GetEntryAssembly();
			}
			if (assembly == null)
			{
				// executed from unit tests or something strange, that require more information
				return true;
			}
			var attributes = assembly.GetCustomAttributes(typeof(DebuggableAttribute), true);
			return attributes.Length != 0 && ((DebuggableAttribute)attributes[0]).IsJITTrackingEnabled;
		}
#endif

		/// <summary>
		/// for internal errors
		/// </summary>
		XTraceEvent(string message)
		{
			_time = DateTime.UtcNow;
			_level = XTraceLevel.Critical;
			_eventId = "XTraceEventDeserialzieError";
			_additionalInformation = message;
		}

		void DeserializeValidation()
		{
			if (_time == default(DateTime))
			{
				throw new Exception("not all fields deserialized: time is empty");
			}
		}

	}


	static class ext
	{
		public static string Escape(this string str)
		{
			if (str == null)
			{
				return str;
			}
			str = str.Replace('"', '`');
			str = str.Replace("\r\n", "\n");
			str = str.Replace("\r", "\n");
			str = str.Replace("\n", "[::nl::]");
			return str;
		}

		public static string Unescape(this string str)
		{
			if (str == null)
			{
				return str;
			}
			str = str.Replace("[::nl::]", "\r\n");
			str = str.Replace('`', '"');
			return str;
		}
	}
}